package com.tiancheng.trade.commom.core.utils;

import lombok.extern.slf4j.Slf4j;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * 消息签名
 *
 * @author kellen
 * @date 2024/5/1
 */
@Slf4j
public final class MessageSign {

    private static final String CONTENT_CHARSET = "UTF-8";

    /**
     * 获取签名
     * @param params
     * @param key
     * @return
     */
    public static Optional<String> get(SortedMap<String, Object> params, String key) {
        try {
            String plain = generateSignPlain(params).toString();
            SecretKeySpec signKey = new SecretKeySpec(key.getBytes(CONTENT_CHARSET), "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(signKey);

            byte[] rawHmac = mac.doFinal(plain.getBytes(CONTENT_CHARSET));

            return Optional.of(Base64.getEncoder().encodeToString(rawHmac));
        } catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException e) {
            log.error(e.getMessage());
            return Optional.empty();
        }
    }

    /**
     * 获取签名
     * @param params
     * @param key
     * @return
     */
    public static Optional<String> getCompatible(SortedMap<String, Object> params, String key) {
        try {
            String plain = generateSignPlain(params).toString();
            plain=plain.replaceAll("\\+", "%20").replaceAll("\\*", "%2A");
            SecretKeySpec signKey = new SecretKeySpec(key.getBytes(CONTENT_CHARSET), "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(signKey);

            byte[] rawHmac = mac.doFinal(plain.getBytes(CONTENT_CHARSET));

            return Optional.of(Base64.getEncoder().encodeToString(rawHmac));
        } catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException e) {
            log.error(e.getMessage());
            return Optional.empty();
        }
    }

    /**
     * 按照key值字典顺序，并用&拼接
     *
     * @param params
     * @return
     */
    private static StringBuilder generateSignPlain(SortedMap<String, Object> params) throws UnsupportedEncodingException {
        StringBuilder plain = new StringBuilder();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            if (entry.getValue() instanceof List) {
                List<Object> list = (List<Object>) entry.getValue();
                if (list != null && list.size() > 0) {
                    plain.append(entry.getKey() + "=");
                    if (list.get(0) instanceof String) {
                        for (int i = 0; i < list.size(); i++) {
                            plain.append(i).append("=").append(list.get(i).toString()).append("&");
                        }
                    } else {
                        for (int i = 0; i < list.size(); i++) {
                            plain.append(generateSignPlain(new TreeMap<>((Map)list.get(i))));
                            plain.append("&");
                        }
                    }
                } else {
                    plain.append(entry.getKey()).append("=&");
                }
            } else if (entry.getValue() instanceof Map) {
                Map<String, Object> map = (Map<String, Object>) entry.getValue();
                if (map != null && map.size() > 0) {
                    plain.append(entry.getKey() + "=").append(generateSignPlain(new TreeMap<>(map))).append("&");
                }
            } else {
                if("sign".equals(entry.getKey())) {
                    continue;
                }
                if(entry.getValue() == null) {
                    plain.append(entry.getKey()).append("=&");
                } else {
                    String encodeStr = URLEncoder.encode(String.valueOf(entry.getValue()), CONTENT_CHARSET);
                    plain.append(entry.getKey() + "=" + encodeStr.replaceAll("\\+", "%20").replaceAll("\\*", "%2A") + "&");
                }
            }
        }
        return plain.length() > 0 ? plain.deleteCharAt(plain.length() - 1) : new StringBuilder();
    }
}
